#include "wm_sspi.h"

static SSPI_HandleTypeDef gsspi;


static HAL_StatusTypeDef SspiRxInit(SSPI_HandleTypeDef *hsspi);
static HAL_StatusTypeDef SspiTxInit(SSPI_HandleTypeDef *hsspi);
static void SSPI_Cfg(SSPI_HandleTypeDef *hsspi);
static void CisInit(SSPI_HandleTypeDef *hsspi);
static void SSDIO_Cfg(SSPI_HandleTypeDef *hsspi);

/**
  * @brief  Initialize the SSPI according to the interface parameter.
  * 
  * @param  type interface type
  *              INTERFACE_TYPE_SSPI : slave spi;
  * 			 INTERFACE_TYPE_SSDIO : slave sdio.
  * 
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SSPI_Init(enum INTERFACE_TYPE type)
{
	SSPI_HandleTypeDef *hsspi = &gsspi;
	
	hsspi->Lock = HAL_UNLOCKED;
	HAL_SSPI_MspInit();
	SspiRxInit(hsspi);
	SspiTxInit(hsspi);
	HAL_NVIC_EnableIRQ(SSPI_IRQn);
	
	hsspi->Instance3 = WRAPPER;
	WRITE_REG(hsspi->Instance3->RXLINKADDR, (uint32_t)(hsspi->RxDesc));
	WRITE_REG(hsspi->Instance3->RXLINKEN, 1);
	WRITE_REG(hsspi->Instance3->RXEN, 1);
	
	WRITE_REG(hsspi->Instance3->TXLINKADDR, (uint32_t)(hsspi->TxDesc));
	WRITE_REG(hsspi->Instance3->TXLINKEN, 1);
	
	hsspi->RxCmdBuf = (uint8_t *)SSPI_CMD_RXBUF_ADDR;
	WRITE_REG(hsspi->Instance3->RXCMDBUFADDR, (uint32_t)(hsspi->RxCmdBuf));
	WRITE_REG(hsspi->Instance3->RXCMDBUFLEN, SSPI_CMD_RXBUF_SIZE);
	WRITE_REG(hsspi->Instance3->RXCMDBUFAVAIL, 1);
	
	WRITE_REG(hsspi->Instance3->IM, 0);
	
	if(type == INTERFACE_TYPE_SSPI)
	{
		hsspi->Instance1 = SSPI;
		SSPI_Cfg(hsspi);
	}
	else
	{
		hsspi->Instance2 = SSDIO;
		SSDIO_Cfg(hsspi);
	}
	
	return HAL_OK;
}

/**
  * @brief  De-Initialize the SSPI peripheral.
  * 
  * @param  
  * 
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SSPI_DeInit(void)
{
	SSPI_HandleTypeDef *hsspi = &gsspi;
	
	HAL_SSPI_MspDeInit();
	HAL_NVIC_DisableIRQ(SSPI_IRQn);
	WRITE_REG(hsspi->Instance3->RXLINKEN, 0);
	WRITE_REG(hsspi->Instance3->RXEN, 0);
	WRITE_REG(hsspi->Instance3->TXLINKEN, 0);
	WRITE_REG(hsspi->Instance3->RXCMDBUFAVAIL, 0);
	WRITE_REG(hsspi->Instance3->IM, 0xF);
	memset(hsspi, 0, sizeof(SSPI_HandleTypeDef));
	return HAL_OK;
}

/**
  * @brief  Initialize the SSPI MSP.
  * @param  
  * 
  * @retval None
  */
__attribute__((weak)) void HAL_SSPI_MspInit(void)
{
	
}

/**
  * @brief  De-Initialize the SSPI MSP.
  * 
  * @param  
  * 
  * @retval None
  */
__attribute__((weak)) void HAL_SSPI_MspDeInit(void)
{
	
}

/**
  * @brief  Transmit an amount of data in non-blocking mode.
  * 
  * @param  pData pointer to data buffer
  * @param  Size amount of data to be sent
  * @param  
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SSPI_Transmit(uint8_t *pData, uint32_t Size)
{
	SSPI_HandleTypeDef *hsspi = &gsspi;
	struct TxBufDesc *tx_desc = hsspi->TxDesc;
	uint32_t txlen = 0, totallen = Size, offset = 0;
	
	__HAL_LOCK(hsspi);
	if ((pData == NULL) || (Size == 0) || (Size > (SSPI_TXBUF_DESC_NUM * SSPI_TXBUF_SIZE)))
	{
		return HAL_ERROR;
	}
	
	while (1)
	{
		if ((tx_desc->valid_ctrl & (1 << 31)) == 0)
		{
			break;
		}
		HAL_Delay(1);
	}
	
	while (!(tx_desc->valid_ctrl & (1 << 31)))
	{
		txlen = (totallen > SSPI_TXBUF_SIZE) ? SSPI_TXBUF_SIZE : totallen;
		memcpy((char *)(tx_desc->buf_addr[0]), pData + offset, txlen);
		tx_desc->buf_info = txlen << 12;
		tx_desc->valid_ctrl = (1 << 31);
		WRITE_REG(hsspi->Instance3->TXEN, 0x01);
		hsspi->TxDesc = (struct TxBufDesc *)tx_desc->next;
		totallen -= txlen;
		offset += txlen;
		if (totallen <= 0)
		{
			break;
		}
	}
	__HAL_UNLOCK(hsspi);
	return HAL_OK;
}

/**
  * @brief  Handle SSPI interrupt request.
  * 
  * @param  
  * 
  * @retval None
  */
void HAL_SSPI_IRQHandler(void)
{
	SSPI_HandleTypeDef *hsspi = &gsspi;
	uint32_t int_flag = hsspi->Instance3->IF;
	
	hsspi->Instance3->IF = int_flag;
	if (int_flag & WRAPPER_IF_RXCMD)
	{
		HAL_SSPI_RxCmdCpltCallback(hsspi->RxCmdBuf);
		WRITE_REG(hsspi->Instance3->RXCMDBUFAVAIL, 0x01);
	}
	
	if (int_flag & WRAPPER_IF_TXDATA)
	{
		HAL_SSPI_TxCpltCallback();
	}
	
	if (int_flag & WRAPPER_IF_RXDATA)
	{
		struct RxBufDesc *rx_desc = hsspi->RxDesc;
		
		while (!(rx_desc->valid_ctrl & (1 << 31)))
		{
			HAL_SSPI_RxDataCpltCallback((uint8_t *)(rx_desc->buf_addr));
			rx_desc->valid_ctrl = (1 << 31);
			WRITE_REG(hsspi->Instance3->RXEN, 0x01);
			hsspi->RxDesc = (struct RxBufDesc *)(rx_desc->next);
		}
	}
}

/**
  * @brief  Rx cmd transfer completed callback.
  * 
  * @param  
  *        
  * @retval None
  */
__attribute__((weak)) void HAL_SSPI_RxCmdCpltCallback(uint8_t *buf)
{
	UNUSED(buf);
}

/**
  * @brief  Tx Transfer completed callback.
  * 
  * @param  
  *        
  * @retval None
  */
__attribute__((weak)) void HAL_SSPI_TxCpltCallback(void)
{
	
}

/**
  * @brief  Rx data transfer completed callback.
  * 
  * @param  
  *        
  * @retval None
  */
__attribute__((weak)) void HAL_SSPI_RxDataCpltCallback(uint8_t *buf)
{
	UNUSED(buf);
}

static HAL_StatusTypeDef SspiRxInit(SSPI_HandleTypeDef *hsspi)
{
	struct RxBufDesc *rx_desc;
	int i;
	
	rx_desc = (struct RxBufDesc *)SSPI_RX_DESC_BASE_ADDR;
	
	for (i = 0; i < SSPI_RXBUF_DESC_NUM; i++)
	{
		rx_desc[i].valid_ctrl = (1 << 31);
		rx_desc[i].buf_addr = (uint32_t)(SSPI_RXBUF_BASE_ADDR + i * SSPI_RXBUF_SIZE);
		if (i == (SSPI_RXBUF_DESC_NUM - 1))
		{
			rx_desc[i].next = (uint32_t)rx_desc;
		}
		else
		{
			rx_desc[i].next = (uint32_t)(rx_desc + 1);
		}
	}
	hsspi->RxDesc = rx_desc;
	return HAL_OK;
}

static HAL_StatusTypeDef SspiTxInit(SSPI_HandleTypeDef *hsspi)
{
	struct TxBufDesc *tx_desc;
	int i;
	
	tx_desc = (struct TxBufDesc *)SSPI_TX_DESC_BASE_ADDR;
	
	for (i = 0; i < SSPI_TXBUF_DESC_NUM; i++)
	{
		tx_desc[i].valid_ctrl = 0;
		tx_desc[i].buf_info = 0;
		tx_desc[i].buf_addr[0] = (uint32_t)(SSPI_TXBUF_BASE_ADDR + i * SSPI_TXBUF_SIZE);
		tx_desc[i].buf_addr[1] = 0;
		tx_desc[i].buf_addr[2] = 0;
		if (i == (SSPI_TXBUF_DESC_NUM - 1))
		{
			tx_desc[i].next = (uint32_t)tx_desc;
		}
		else
		{
			tx_desc[i].next = (uint32_t)(tx_desc + 1);
		}
	}
	hsspi->TxDesc = tx_desc;
	return HAL_OK;
}

static void SSPI_Cfg(SSPI_HandleTypeDef *hsspi)
{
	WRITE_REG(hsspi->Instance1->RSTFIFO, 0x01);
	WRITE_REG(hsspi->Instance1->CR1, 0);
	WRITE_REG(hsspi->Instance1->CR2, 0);
	WRITE_REG(hsspi->Instance1->IM, 0x03);
	WRITE_REG(hsspi->Instance1->IF, 0x03);
}

static void CisInit(SSPI_HandleTypeDef *hsspi)
{
	CIS_FN0_TypeDef *cis0 = (CIS_FN0_TypeDef *)(CIS_FUN0_ADDR);
	CIS_FN1_TypeDef *cis1 = (CIS_FN1_TypeDef *)(CIS_FUN1_ADDR);
	
	cis0->FUNCID = 0x000C0221;
	cis0->FUNCE = 0x00000422;
	cis0->FUNCE_MAXBLK = 0x04203208;
	cis0->MANFID_MID = 0x53470296;
	cis0->END = 0x000000FF;
	
	cis1->FUNCID = 0x000C0221;
	cis1->FUNCE = 0x01012a22;
	cis1->FUNCE_VER = 0x00000011;
	cis1->FUNCE_NSN = 0x02000000;
	cis1->FUNCE_CSASIZE = 0x08000300;
	cis1->FUNCE_OCR = 0x00FF8000;
	cis1->FUNCE_MINPWR = 0x010f0a08;
	cis1->FUNCE_STANDBY = 0x00000101;
	cis1->FUNCE_OPTBW = 0x00000000;
	cis1->FUNCE_NTIMEOUT = 0x00000000;
	cis1->FUNCE_AVGPWR1 = 0x00000000;
	cis1->FUNCE_AVGPWR2 = 0x00000000;
	cis1->END = 0x000000ff;
}

static void SSDIO_Cfg(SSPI_HandleTypeDef *hsspi)
{
	uint32_t v;
	
	CisInit(hsspi);
	
	WRITE_REG(hsspi->Instance2->CIS0, (CIS_FUN0_ADDR - 0x1000));
	WRITE_REG(hsspi->Instance2->CIS1, (CIS_FUN1_ADDR - 0x2000));
	
	v = READ_REG(hsspi->Instance2->CIA);
	WRITE_REG(hsspi->Instance2->CIA, ((v & 0xFFFFF000) | 0x232));
	
	WRITE_REG(hsspi->Instance2->PROG, 0x02FD);
}
